package com.gitee.l0km.codegen.decorator;

import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.l0km.codegen.annotations.AnnotationRuntimeException;
import com.gitee.l0km.codegen.base.Method;
import com.gitee.l0km.codegen.base.NewSourceInfoAbstract;
import com.gitee.l0km.codegen.base.Method.Parameter;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import static com.gitee.l0km.codegen.decorator.MXBeanConfiguration.MXBCONFIG;
import static com.gitee.l0km.com4j.basex.bean.jdk.BeanPropertySupportImpl.BEAN_SUPPORT;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * MBean生成器{@code NewSourceInfo}实现
 * @author guyadong
 *
 * @param <T>
 */
public class MXBeanInterface<T> extends NewSourceInfoAbstract<T> {
	private static final Logger logger = LoggerFactory.getLogger(MXBeanInterface.class);
	private final List<String> includeFields;
	private final List<String> excludeFields;
	public MXBeanInterface(Class<? extends T> refClass) {
		super(null, refClass, null);
		includeFields = MXBCONFIG.getIncludeFields().get(refClass);
		excludeFields = MXBCONFIG.getExcludeFields().get(refClass);
	}
	@Override
	protected Map<String, PropertyDescriptor> readProperties() {
		return BEAN_SUPPORT.getProperties(this.refClass, 0, true);
	}
	@Override
	public boolean compile() {
		boolean compileOk=false;
		try{
			super.compile();
			for (Method method : methodsNeedGenerated) {
				compile(method);
			}
			compileOk = true;
		} catch(AnnotationRuntimeException e){
			logger.error(e.toString());
		} 
		return compileOk;
	}
	private final void compile(Method method) {
		for (Parameter param : method.getParameters()) {
			compile(param);
		}
	}

	private final void compile(Parameter param) {

	}
	@Override
	protected void checkClass(Class<T> interfaceClass, Class<? extends T> refClass, Class<? extends T> baseClass) {
		checkNotNull(refClass,"refClass isn ull");
	}

	private boolean fieldFilter(PropertyDescriptor descriptor){
		if(includeFields != null && !includeFields.isEmpty()){
			return includeFields.contains(descriptor.getName());
		}
		if(includeFields != null && !excludeFields.isEmpty()){
			return !excludeFields.contains(descriptor.getName());
		}
		return true;
	}
	@Override
	protected void createMethodsNeedGenerated() {
		Map<String, PropertyDescriptor> properties = BEAN_SUPPORT.getProperties(refClass, 3, true);
		final Set<java.lang.reflect.Method> propRWMethods = Sets.newHashSet();
		for(PropertyDescriptor descriptor:properties.values()){
			java.lang.reflect.Method readMethod = descriptor.getReadMethod();
			boolean needAdd = fieldFilter(descriptor);
			if(readMethod != null){
				propRWMethods.add(readMethod);
				if(MXBCONFIG.isNeedField() && needAdd){
					methodsNeedGenerated.add(new Method(readMethod,
							paramTable.getParameterNamesUnchecked(readMethod.getName(), readMethod.getParameterTypes())));
				}
			}
			java.lang.reflect.Method writeMethod = descriptor.getWriteMethod();
			if(writeMethod != null){
				propRWMethods.add(writeMethod);
				if(MXBCONFIG.isNeedField() && needAdd){
					methodsNeedGenerated.add(new Method(writeMethod,
							paramTable.getParameterNamesUnchecked(writeMethod.getName(), writeMethod.getParameterTypes())));
				}
			}
		}
	
		if(MXBCONFIG.isNeedMethod()){
			
			Iterable<java.lang.reflect.Method> userMethods = Iterables.filter(Arrays.asList(refClass.getMethods()), new UserPublicMethodFilter());
			Iterable<Method> methods = Iterables.transform(userMethods, 
					new Function<java.lang.reflect.Method, Method>() {
						@Override
						public Method apply(java.lang.reflect.Method input) {
							return new Method(input,
									paramTable.getParameterNamesUnchecked(input.getName(), input.getParameterTypes()));
						}
			});
			Predicate<java.lang.reflect.Method> noFieldMethodFilter = new Predicate<java.lang.reflect.Method>() {

				@Override
				public boolean apply(java.lang.reflect.Method input) {
					return !propRWMethods.contains(input);
				}
			};
			ArrayList<Method> filtered = 
					Lists.newArrayList(
							Iterables.filter(methods, Predicates.compose(Predicates.and(noFieldMethodFilter,new MethodNameFilter(refClass,MXBCONFIG)), Method.TO_REFLECT_METHOD)));
			methodsNeedGenerated.addAll(filtered);
		}
	}
}
